package testing;

import static view.Constants.BUFFER_SIZE;
import static view.Constants.MAPPING;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import controller.Controller;

import core.AbstractCompresser;
import core.CompressedBuffer;
import core.CompressedChar;
import core.UnsupportedCompression;

/** the compressed file will be of the format
 *  8-byte-tablePos, fileNum
 *  nameLength, fileName, numBytes, padding
 *   
 *  decoding table
 * 
 * @author mostafa
 *
 */
@SuppressWarnings("unused")
public class FolderCompresser extends  AbstractCompresser
{
	private File[] files;
	private byte[] buffer;
	private int fileNum;
	private int fileCounter;
	private int offset;

	public FolderCompresser(String source, String compressed) throws UnsupportedCompression
	{
		super(source, compressed); // TODO remove
		File file = new File(source);
		file.mkdir();
		if (! file.exists() || ! file.isDirectory())
			throw new UnsupportedCompression();
		files = file.listFiles();
		for (File f : files)
		{
			if (f.isDirectory())
			{
				throw new UnsupportedCompression("folder should only contain files");
			}
		}
		this.fileNum = files.length;
		this.offset = 0;
		this.fileCounter = 0;
		FileOutputStream opStream;
		try {
			opStream = new FileOutputStream(file.getPath() + ".txt");
			writer = opStream.getChannel();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		buffer = new byte[BUFFER_SIZE];
	}

	public void compress()
	{
		CompressedBuffer opBuffer = new CompressedBuffer();
		int offset = 0;
		for (int i = 0; i < files.length; i++)
		{
			File file = files[i];
			try {
				in = new BufferedInputStream(new FileInputStream(file));
				compress(in, opBuffer);
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		try {
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void compress(BufferedInputStream in, CompressedBuffer opBuffer) throws IOException
	{
		CompressedChar[] chars = Controller.getInstance().getChars();
		int numRead = 0;
		while ((numRead = in.read(buffer)) != -1)
		{
			for (int i = 0; i < numRead; i++)
			{
				if (offset == 0)
					offset = opBuffer.add(chars[buffer[i]+MAPPING], chars[buffer[i]+MAPPING].length);
				if (offset != 0)
				{
					// empty buffer in the file
					writer.write(ByteBuffer.wrap(opBuffer.bytes()));
					opBuffer.reset();
					offset = opBuffer.add(chars[buffer[i]+MAPPING], offset);
				}
			}
		}
		writer.write(ByteBuffer.wrap(opBuffer.bytes(), 0, opBuffer.rows()));
		writer.position(0);
		writer.write(ByteBuffer.wrap(new byte[]{opBuffer.getRemainingColumns()}));
	}

	public void writeTable()
	{
		CompressedChar[] table = Controller.getInstance().getTable();
		// nameLength, fileName, numBytes, padding
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < files.length; i++)
			buffer.append(files[i].getName());
		String dat = buffer.toString();
		byte[] fileDat = new byte[dat.length() + (files.length * 10)]; // name length, padding, numBytes * 8
		int k = 0;
		for (int i = 0, j = 0; i < files.length; i++)
		{
			int len = files[i].getName().length();
			fileDat[j++] = (byte) len;
			for (int t = k; t < len + k; t++)
				fileDat[j++] = (byte) dat.charAt(t);
			k = k + len;
			fileDat[j++] = 0; // for now bas TODO
			fileDat[j++] = 0; // TODO
		}
		int size = table.length * 2 + 2; // add a one offset for the padding and one for the length
		byte[] data = new byte[size];
		data[0] = (byte) table.length;
		for (int i = 1, n = table.length + 1; i < n; i++)
		{
			CompressedChar cChar = table[i - 1];
			data[2*i - 1] = cChar.ch;
			data[2*i] = cChar.length;
		}
		try {
			writer.write(ByteBuffer.wrap(data));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args)
	{
		try {
			new FolderCompresser("sossa", "sossa.out.txt").writeTable();
		} catch (UnsupportedCompression e) {
			e.printStackTrace();
		}
	}

	@Override
	public void noTable() {
		// TODO Auto-generated method stub
		
	}
}
